package com.luom.fsp.service.impl;
import java.math.BigDecimal;
import java.util.*;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luom.fsp.common.ErrorCode;
import com.luom.fsp.constant.CommonConstant;
import com.luom.fsp.exception.BusinessException;
import com.luom.fsp.exception.ThrowUtils;
import com.luom.fsp.mapper.UserCouponMapper;
import com.luom.fsp.model.dto.userCoupon.UserCouponQueryRequest;
import com.luom.fsp.model.entity.Coupon;
import com.luom.fsp.model.entity.User;
import com.luom.fsp.model.entity.UserCoupon;
import com.luom.fsp.model.enums.UserCouponStatusEnum;
import com.luom.fsp.model.vo.CouponVO;
import com.luom.fsp.model.vo.UserCouponVO;
import com.luom.fsp.service.CouponService;
import com.luom.fsp.service.UserCouponService;
import com.luom.fsp.service.UserService;
import com.luom.fsp.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;

import static com.luom.fsp.constant.RedisConstant.SECKILL_COUPON_LOCK_KEY;

/**
 * 用户-优惠券关联表服务实现
 *
*/
@Service
@Slf4j
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements UserCouponService {

    @Resource
    private UserService userService;

    @Resource
    private CouponService couponService;

    @Resource
    private UserCouponMapper userCouponMapper;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 校验数据
     *
     * @param userCoupon
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validUserCoupon(UserCoupon userCoupon, boolean add) {
        ThrowUtils.throwIf(userCoupon == null, ErrorCode.PARAMS_ERROR);

        Long id = userCoupon.getId();
        Long userId = userCoupon.getUserId();
        Long couponId = userCoupon.getCouponId();
        String status = userCoupon.getStatus();
        Long orderId = userCoupon.getOrderId();

        // 创建数据时，参数不能为空
        if (add) {
            ThrowUtils.throwIf(ObjectUtils.isEmpty(userId), ErrorCode.PARAMS_ERROR, "用户不能为空");
            ThrowUtils.throwIf(ObjectUtils.isEmpty(couponId), ErrorCode.PARAMS_ERROR, "优惠券不能为空");
        }
        // 修改数据时，有参数则校验
        if (StringUtils.isNotBlank(status)) {
            List<String> values = UserCouponStatusEnum.getValues();
            boolean validType = values.contains(status);
            ThrowUtils.throwIf(!validType, ErrorCode.PARAMS_ERROR, "无效的状态");
        }
        if (ObjectUtils.isNotEmpty(orderId)) {
            ThrowUtils.throwIf(orderId < 0, ErrorCode.PARAMS_ERROR, "订单异常");
        }
    }

    /**
     * 获取查询条件
     *
     * @param userCouponQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<UserCoupon> getQueryWrapper(UserCouponQueryRequest userCouponQueryRequest) {

        QueryWrapper<UserCoupon> queryWrapper = new QueryWrapper<>();
        if (userCouponQueryRequest == null) {
            return queryWrapper;
        }
        Long id = userCouponQueryRequest.getId();
        Long userId = userCouponQueryRequest.getUserId();
        Long couponId = userCouponQueryRequest.getCouponId();
        String status = userCouponQueryRequest.getStatus();
        Long orderId = userCouponQueryRequest.getOrderId();
        String sortField = userCouponQueryRequest.getSortField();
        String sortOrder = userCouponQueryRequest.getSortOrder();

        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(couponId), "couponId", couponId);
        if (StringUtils.isNotBlank(status)){
            List<String> values = UserCouponStatusEnum.getValues();
            boolean validType = values.contains(status);
            ThrowUtils.throwIf(!validType, ErrorCode.PARAMS_ERROR, "无效的状态");
            queryWrapper.eq(ObjectUtils.isNotEmpty(status), "status", status);
        }
        queryWrapper.eq(ObjectUtils.isNotEmpty(orderId), "orderId", orderId);

        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取用户-优惠券关联表封装
     *
     * @param userCoupon
     * @param request
     * @return
     */
    @Override
    public UserCouponVO getUserCouponVO(UserCoupon userCoupon, HttpServletRequest request) {
        // 对象转封装类
        UserCouponVO userCouponVO = UserCouponVO.objToVo(userCoupon);

        // 填充用户信息
        User user = userService.getById(userCoupon.getUserId());
        userCouponVO.setUser(userService.getUserVO(user));
        // 填充优惠券信息
        Coupon coupon = couponService.getById(userCoupon.getCouponId());
        userCouponVO.setCoupon(couponService.getCouponVO(coupon, request));

        return userCouponVO;
    }

    /**
     * 分页获取用户-优惠券关联表封装
     *
     * @param userCouponPage
     * @param request
     * @return
     */
    @Override
    public Page<UserCouponVO> getUserCouponVOPage(Page<UserCoupon> userCouponPage, HttpServletRequest request) {
        List<UserCoupon> userCouponList = userCouponPage.getRecords();
        Page<UserCouponVO> userCouponVOPage = new Page<>(userCouponPage.getCurrent(), userCouponPage.getSize(), userCouponPage.getTotal());
        if (CollUtil.isEmpty(userCouponList)) {
            return userCouponVOPage;
        }
        // 对象列表 => 封装对象列表
        List<UserCouponVO> userCouponVOList = userCouponList.stream().map(UserCouponVO::objToVo).collect(Collectors.toList());

        // 1. 关联查询用户信息
        Set<Long> userIdSet = userCouponList.stream().map(UserCoupon::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 2.关联查询优惠券信息
        Set<Long> couponIdSet = userCouponList.stream().map(UserCoupon::getCouponId).collect(Collectors.toSet());
        Map<Long, List<Coupon>> couponIdCouponListMap = couponService.listByIds(couponIdSet).stream()
                .collect(Collectors.groupingBy(Coupon::getId));

        // 填充信息
        userCouponVOList.forEach(userCouponVO -> {
            // 获取用户信息
            Long userId = userCouponVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            userCouponVO.setUser(userService.getUserVO(user));

            // 获取优惠券信息
            Long couponId = userCouponVO.getCouponId();
            Coupon coupon = Optional.ofNullable(couponIdCouponListMap.get(couponId))
                    .filter(list -> !list.isEmpty())
                    .map(list -> list.get(0))
                    .orElse(null);
            userCouponVO.setCoupon(CouponVO.objToVo(coupon));
        });

        userCouponVOPage.setRecords(userCouponVOList);
        return userCouponVOPage;
    }

    /**
     * 购买优惠券
     * @param userCoupon
     * @param request
     * @return
     */
    @Override
    public long addUserCoupon(UserCoupon userCoupon, HttpServletRequest request) {
        // 数据校验
        this.validUserCoupon(userCoupon, true);
        Long userId = userService.getLoginUser(request).getId();
        // 如果不是管理员，默认为当前用户
        if (!userService.isAdmin(request)) {
            userCoupon.setUserId(userId);
        }

        Coupon coupon = couponService.getById(userCoupon.getCouponId());
        // 如果是秒杀券，超过秒杀时间
        if (coupon.getSeckillStartTime() != null && coupon.getSeckillEndTime() != null){
            if (coupon.getSeckillEndTime().getTime() < System.currentTimeMillis()) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "秒杀券已过期");
            }
        }
        // 如果是普通券，结束时间小于现在，也无法领取
        if (coupon.getEndTime() != null && coupon.getEndTime().getTime() < System.currentTimeMillis()) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "优惠券已过期");
        }
        //判断库存
        if (coupon.getStock() <= 0) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "库存不足");
        }

        // 分布式锁
        String lockKey = SECKILL_COUPON_LOCK_KEY + userId;
        RLock lock = redissonClient.getLock(lockKey);
        boolean isLock = lock.tryLock();
        if (!isLock){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "请勿重复下单");
        }
        try {
            //接口代理对象
            UserCouponService proxy = (UserCouponService) AopContext.currentProxy();
            return proxy.createUserCoupon(userCoupon, coupon);
        }finally {
            lock.unlock();
        }

        /*// 同步锁
        synchronized (userId.toString().intern()){
            //接口代理对象
            UserCouponService proxy = (UserCouponService) AopContext.currentProxy();
            return proxy.createUserCoupon(userCoupon, coupon);
        }*/
    }

    // 悲观锁确保线程安全
    @Transactional
    @Override
    public long createUserCoupon(UserCoupon userCoupon, Coupon coupon) {
        //乐观锁减库存
        boolean b = couponService.update().setSql("stock = stock - 1")
                        .eq("id", coupon.getId())
                        .gt("stock", 0)
                        .update();
        ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR, "库存不足");

        // 一人一单
        QueryWrapper<UserCoupon> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userCoupon.getUserId());
        queryWrapper.eq("couponId", userCoupon.getCouponId());
        if (this.getOne(queryWrapper) != null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您已拥有该优惠券");
        }

        // 写入数据库
        boolean result = this.save(userCoupon);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回新写入的数据 id
        return userCoupon.getId();
    }

    /**
     * 获取当前金额用户可用优惠券列表
     * @param money
     * @param request
     * @return
     */
    @Override
    public List<CouponVO> listUserCouponVOByPage(BigDecimal money, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 查询用户未使用的优惠券
        List<UserCoupon> userCoupons = userCouponMapper.selectList(new QueryWrapper<UserCoupon>()
                .eq("userId", loginUser.getId())
                .eq("status", UserCouponStatusEnum.UNUSED.getValue())
                .isNull("orderId"));

        if (CollUtil.isEmpty(userCoupons)) {
            return Collections.emptyList();
        }

        // 提取优惠券ID
        List<Long> couponIds = userCoupons.stream()
                .map(UserCoupon::getCouponId)
                .collect(Collectors.toList());

        // 查询有效优惠券
        List<Coupon> validCoupons = couponService.list(new QueryWrapper<Coupon>()
                .in("id", couponIds)
                .le("minAmount", money)
                .le("startTime", new Date())
                .ge("endTime", new Date()));

        return validCoupons.stream()
                .map(CouponVO::objToVo)
                .collect(Collectors.toList());
    }


}
